// Utility for binding to dictionaries of datapumps
// Copyright 2006 (C) Ralph Thomas

#include <datapumps/dictionary_helper.h>

namespace datapumps {

	static void delete_helpers(dictionary_helper_t::helper_list_t& list)
	{

		dictionary_helper_t::helper_list_t::const_iterator i = list.begin();
		while( i != list.end() ) {
			delete *i;
			i++;
		}
		list.clear();
	}
	
	static void update_value(
					adobe::dictionary_t&			dict,
					adobe::name_t					name,
					bool&							init,
					dictionary_helper_t::monitor_t	monitor,
					const adobe::value_t&			value)
	{
		if (dict.count(name) == 0)
			dict.insert(adobe::dictionary_t::value_type(name, value));
		else
			dict = dict.set(name, value);
		if (!init && monitor)
			monitor(dict);
	}

	static void update_dict(
					adobe::sheet_t& 					sheet,
					adobe::dictionary_t&				dict,
					dictionary_helper_t::helper_list_t&	helpers,
					dictionary_helper_t::monitor_t		monitor,
					bool&								init,
					const adobe::value_t&				val)
	{
		delete_helpers(helpers);
		dict.clear();
		if( val.type() != typeid(adobe::dictionary_t) )
		{
			//
			// No dictionary. Tell the monitor that it now has an empty
			// dictionary and bail out.
			// 
			monitor(dict);
			return;
		}
		adobe::dictionary_t source = val.get<adobe::dictionary_t>();
		init = true;
		adobe::dictionary_t::iterator i = source.begin();
		//
		// Create a binder helper for each kv-pair in the dictionary.
		// 
		while( i != source.end() ) {
			adobe::binder_helper_t* bh = new adobe::binder_helper_t(sheet);
			bh->initialize(i->second, boost::bind(&update_value, boost::ref(dict), i->first, boost::ref(init), monitor, _1), 0);
			helpers.push_back(bh);
			i++;
		}
		//
		// From now on update_value may call monitor.
		// 
		init = false;
		//
		// Inform the monitor of the new dictionary.
		//
		monitor(dict);
	}
	
	dictionary_helper_t::dictionary_helper_t(adobe::sheet_t& sheet)
	: sheet_m(sheet), bind_m(sheet) {}
	dictionary_helper_t::~dictionary_helper_t()
	{
		delete_helpers(helpers_m);
	}
	void dictionary_helper_t::initialize(const adobe::value_t& value, const monitor_t& monitor)
	{
		bind_m.initialize(value, boost::bind(&update_dict, boost::ref(sheet_m), boost::ref(dict_m), boost::ref(helpers_m), monitor, boost::ref(init_m), _1), 0);
	}
	void dictionary_helper_t::substitute(const adobe::name_t& name, const adobe::value_t& value)
	{
		bind_m.substitute(name, value);
		helper_list_t::const_iterator i = helpers_m.begin();
		while( i != helpers_m.end() ) {
			(*i)->substitute(name, value);
			i++;
		}
	}
};
